#!/usr/bin/python
# -*- coding: UTF-8 -*-

import sys
import os
import struct
import binascii
import datetime
import time

from bluepy.btle import Scanner, DefaultDelegate, UUID, Peripheral


#######################################################################################################

# ble_devicemac = "10:00:00:00:03:89"
ble_devicemac = "97:99:66:66:99:99"

ble_oadbinfilename = "oad.bin"



ble_oad_loop_head = 4
notify_wait_time = 0.5
#######################################################################################################
ble_notify_data_str = 0

ble_img_identify_uuid = "f000ffc1-0451-4000-b000-000000000000"
ble_img_block_uuid = "f000ffc2-0451-4000-b000-000000000000"
ble_oad_extended_ctl_uuid = "f000ffc5-0451-4000-b000-000000000000"

ble_get_blocksize_cmd = (0x01).to_bytes(1, byteorder="big")
ble_get_deviceid_cmd = (0x10).to_bytes(1, byteorder="big")
ble_start_oad_cmd = (0x03).to_bytes(1, byteorder="big")
ble_start_oad_enable = (0x04).to_bytes(1, byteorder="big")

ble_data_success_rslt = (0x00).to_bytes(1, byteorder="big")
ble_data_end_rslt = (0x0E).to_bytes(1, byteorder="big")

ble_oadbinfile = 0
ble_oadbinfile_size = 0

#######################################################################################################

def ble_open_oadbin(filename):
	try:
		fp = open(filename, "rb")
		print("%s open success" %(filename))
		return fp

	except IOError:
		print("%s open failed" %(filename))

def ble_get_filesize(filename):
	fsize = os.path.getsize(filename)
	return fsize
	# return round(fsize, 2)


class BLEDelegate(DefaultDelegate):
    # Constructor (run once on startup)
    def __init__(self, params):
        DefaultDelegate.__init__(self)
        print(params)

    # func is called on notifications
    def handleNotification(self, cHandle, data):
        datastr = str(binascii.b2a_hex(data))
        # datastr = str(data)
        datastr.encode('utf-8')
        # print("Notification from Handle: 0x" + format(cHandle, '02X') + " Value: " + datastr +  " " + str(datetime.datetime.now()))

        global ble_notify_data_str
        ble_notify_data_str = data
        # print(ble_notify_data_str)


def ble_connect_by_macaddr(macaddr):
	bledevice = Peripheral(macaddr, "public")

	return bledevice



def ble_turn_on_notify(bledevice, myuuid):
	bledevice.setDelegate(BLEDelegate(1234))

	try:
		ch = bledevice.getCharacteristics(uuid=myuuid)[0]
		# print("Handle is 0x" + format(ch.getHandle(), '02x'))
		notichhd = ch.getHandle() + 1

		# print("Notify handle is 0x" + format(notichhd, '02x'))

		bledevice.writeCharacteristic(notichhd, struct.pack('<bb', 0x01, 0x00))

		print("Notice(0x%s) turned on" %format(notichhd, '02x'))

		return ch

	except IOError:
		print("ble_turn_on_notify IOError")
	# finally:
	# 	print("finally")

def ble_write_by_handle_and_wait_notify(bledevice, handle, data):
	try:
		handle.write(data)
		while True:
			
			if bledevice.waitForNotifications(notify_wait_time):
				break
			else:
				print("-- No notify")
				# time.sleep(10)
				continue
		return ble_notify_data_str

	except IOError:
		print("ble_write_by_handle_and_wait_notify IOError")
	# finally:
	# 	print("finally")

def ble_get_oad_head_data(oadbin_data):
	# TIOADEoadImageIdentificationValue: first 8 bin bytes
	oad_head = oadbin_data[0:8]
	# TIOADEoadBIMVersion: 1 byte
	oad_head += oadbin_data[12:13]
	# TIOADEoadImageHeaderVersion: 1 byte
	oad_head += oadbin_data[13:14]
	# TIOADEoadImageInformation: 4 bytes
	oad_head += oadbin_data[16:20]
	# unknow: 4 bytes
	oad_head += oadbin_data[24:28]
	# TIOADEoadImageSoftwareVersion: 4 bytes
	oad_head += oadbin_data[32:36]
	# return oad_head

	return oad_head# .to_bytes(22, byteorder="big")

def ble_end(ble_device):
	ble_device.disconnect()
	sys.exit()

def ble_oad_mac_oadbin(mac, oadbin):

	ble_mtu = 255 #20 #20


	print("Main start")


	ble_oadbinfile = ble_open_oadbin(oadbin)

	# with open("oad.bin.read", "wb") as f:
	# 	f.write(ble_oadbinfile.read())

	oaddata = ble_oadbinfile.read()
	ble_oadbinfile.close()

	ble_oadbinfile_size = len(oaddata) #ble_get_filesize(ble_oadbinfilename)



	head_data = ble_get_oad_head_data(oaddata)


	print(head_data)


	# Basic connect
	ble_device = ble_connect_by_macaddr(mac)

	a = ble_device.setMTU(ble_mtu)
	print(a)

	# Turn on corresponding service notify
	img_identify_handle = ble_turn_on_notify(ble_device, ble_img_identify_uuid)
	img_block_handle = ble_turn_on_notify(ble_device, ble_img_block_uuid)
	oad_extended_ctl_handle = ble_turn_on_notify(ble_device, ble_oad_extended_ctl_uuid)

	# Get block size
	block_info = ble_write_by_handle_and_wait_notify(ble_device, oad_extended_ctl_handle, ble_get_blocksize_cmd)
	device_id = ble_write_by_handle_and_wait_notify(ble_device, oad_extended_ctl_handle, ble_get_deviceid_cmd)


	ble_mtu = int().from_bytes(block_info[1:2], byteorder='big', signed=False)

	print("MTU change to %d" %(ble_mtu))

	# ble_end()
	# print("block info %s" %(block_info))
	print("device id %s" %(device_id))

	## Reqest MTU


	identify_rslt = ble_write_by_handle_and_wait_notify(ble_device, img_identify_handle, head_data)

	if identify_rslt != ble_data_success_rslt:
		print("Head data error: %s" %(identify_rslt))
		ble_end(ble_device)

	start_oad_rslt = ble_write_by_handle_and_wait_notify(ble_device, oad_extended_ctl_handle, ble_start_oad_cmd)
	print(start_oad_rslt)
	print(start_oad_rslt[2:6])

	if start_oad_rslt[1:2] != ble_data_success_rslt:
		print("Start oad failed")
		ble_end(ble_device)


	# OAD bin file loop trans
	ble_mtu -= ble_oad_loop_head

	index_1 = 0
	index_2 = index_1 + ble_mtu

	print("oad data len %d" %(len(oaddata)))



	oad_data = (0x000000004F414420494D472021DDF9A60101FEFF).to_bytes(20, byteorder="big")

	print_flag = 0
	while True:

		print_flag += 1

		if index_2 > ble_oadbinfile_size:
			break
		else:
			tmp_data = start_oad_rslt[2:6] + oaddata[index_1: index_2]


			start_oad_rslt = ble_write_by_handle_and_wait_notify(ble_device, img_block_handle, tmp_data)

			index_1 += ble_mtu

			if index_2 + ble_mtu > ble_oadbinfile_size:
				index_2 = ble_oadbinfile_size
			else:
				index_2 += ble_mtu

			if start_oad_rslt[1:2] == ble_data_success_rslt:
				if print_flag % 2 == 0:
					print("...%d%%" %(float(index_2 * 100 / ble_oadbinfile_size)), end='', flush=True)
				else:
					print("---%d%%" %(float(index_2 * 100 / ble_oadbinfile_size)), end='', flush=True)
				# print("...%d" %(float(index_2 * 100 / ble_oadbinfile_size)), end='', flush=True)
				# fflush()
				continue
			elif start_oad_rslt[1:2] == ble_data_end_rslt:
				print("\r\nOAD status finished")
				break
			else :#if start_oad_rslt[1:2] != ble_data_success_rslt:
				print("\r\nOAD status failed")
				ble_end(ble_device)
			



	ble_oad_enable_rslt = ble_write_by_handle_and_wait_notify(ble_device, oad_extended_ctl_handle, ble_start_oad_enable)
	print(ble_oad_enable_rslt)


	ble_end(ble_device)
#######################################################################################################

if __name__ == '__main__':    
	ble_oad_mac_oadbin(sys.argv[1], sys.argv[2])




